export {}

function getSchool(x: string, y: number, z: string) {
  let a: number;
  let b: number;
  let c: number;
  return {a, b, c, p:{age:123,name:'ahhh'}}
}


const state = () => {
  return {
    count: 0,
    hobby: []
  }
};


//上面这样的返回值的类型可能随时变动, 怎么一劳永逸的获取到返回值的类型?
type t1 = ReturnType<typeof getSchool>; // {p: {name: string, age: number}, a: number, b: number, c: number}
type t2 = ReturnType<typeof state>; // {count: number, hobby: any[]}


//这个ReturnType 怎么实现的呢?

/** 1. 获取返回值类型*/
// type ReturnType<T> = T extends (...args: any[]) => any/**是否是(满足)一个函数*/ ? string : never;
//如果我们想拿到返回值的类型 怎么去做呢？
//这个时候可以使用infer告诉ts 我们要推导返回值的类型
type ReturnType<T extends/**← 约束*/ (...args: any) => any> = T extends/**← 是否满足*/ (...args: any[]) => infer R ? R : any/*← 这些撒都可以 反正走不到这里 因为前面已经约束好了*/;
//↑ 这里的 R，类似于 [k in keyof T] 的k一样 不需要提前声明
/*
比如 type t1 = ReturnType<typeof getSchool>;
此时 T 就是getSchool的类型
T extends (...args: any[]) => infer R 的时候会把 T（即function getSchool(x: string, y: number, z: string){...}） 和 `(...args: any[]) => infer R` 做一个比较
比较的时候 前面肯定ok(符合)，
对于返回值来说呢，它会拿这个返回值去做一个推断（你把infer放在哪 它（ts）就会去推断哪的值），宾取名叫R
最后呢 就把R的值返给我们了
如果不满足就是any
*/

/** 注意和 07.Advanced Types高级类型/09.映射类型Mapped types/00-5.Record和mapping.ts 中mapping的例子进行区分
 *  因为我们上面的ReturnType是必须手动传参的(<>), 而我们mapping因为是写在函数里的, 每次调用的时候就会自动传参
 *  , 故mapping里无需通过infer关键字, 而我们这里必须通过infer关键字
 *  , 【infer 一般用在纯类型计算中】 */


/** 2. 获取参数的类型*/
type Parameters<T extends/**← 约束*/ (...args: any) => any> = T extends/**← 是否满足*/ (...args: infer P) => any ? P : any/*← 这些撒都可以 反正走不到这里 因为前面已经约束好了*/;
type t3 = Parameters<typeof getSchool>; //[string, number, string]



/** 3. 获取实例类型*/
/** emmm... Person其实就是实例的类型 这感觉很多余(因为Person这个类作为类型使用的时候 本身就是描述实例的) */
class Person {
  name: string;
  age: number;
}

type MyInstanceType = InstanceType<typeof Person>; // Person

// type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;
// type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;



/** 4. 获取构造函数的参数*/
class Person2 {
  name: string;
  age: number;
  constructor(name: string, age: string) {

  }
}

type MyConstructorParameter = ConstructorParameters<typeof Person>;
type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : any;



//todo 什么情况下是不能推导的?
